package jee.boot.module.iexp.task.impl;

import com.github.pagehelper.util.StringUtil;
import jee.boot.common.basic.DataEntity;
import jee.boot.common.basic.ServiceException;
import jee.boot.common.properties.ProjectConfig;
import jee.boot.common.remote.RemoteEndpoint;
import jee.boot.common.standard.AccountHandler;
import jee.boot.common.standard.AuthorityHandler;
import jee.boot.common.standard.DocumentStorageClient;
import jee.boot.common.utils.DateUtils;
import jee.boot.common.utils.JsonMapper;
import jee.boot.common.utils.SpringContextHolder;
import jee.boot.common.utils.StringUtils;
import jee.boot.entity.task.*;
import jee.boot.entity.file.Attachment;
import jee.boot.module.iexp.excel.excel.ExcelExport;
import jee.boot.module.iexp.excel.template.ExportTemplateExcelExecutor;
import jee.boot.module.iexp.service.IexpTaskService;
import jee.boot.module.iexp.task.*;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 导出任务运行线程
 */
public class EmpRunnable extends BasicTaskRunnable implements Runnable{
    private Logger logger = LoggerFactory.getLogger(TaskListener.class);
    public EmpRunnable(IexpTask task, IexpTaskService iexpTaskService, DocumentStorageClient fileService, AuthorityHandler authorityHandler, AccountHandler accountHandler, RemoteEndpoint remoteEndpoint, ProjectConfig projectConfig) {
        this.task = task;
        this.iexpTaskService = iexpTaskService;
        this.fileService = fileService;
        super.authorityHandler=authorityHandler;
        super.accountHandler=accountHandler;
        super.remoteEndpoint=remoteEndpoint;
        super.projectConfig=projectConfig;
    }

    private IexpTaskService iexpTaskService;
    private IexpTask task;
    private DocumentStorageClient fileService;

    @Override
    public void run() {
        logger.debug("后台导出任务[" + task.getName() + "(" + task.getId() + ")]开始执行");
        configThreadPermission(task);
        task.setStartDate(new Date());
        ExportExecuteService service = null;
        try {
            String delegatedClass = task.getDelegatedClass();
            if (StringUtils.isBlank(delegatedClass)) {
                throw new Exception("委托服务类[delegatedClass]不能为空");
            }
            // 全路径,需要使用反射
            if (StringUtils.contains(delegatedClass, ".")) {
                Class<?> classService = Class.forName(delegatedClass);
                Object classObject = classService.newInstance();
                if (!(classObject instanceof ExportExecuteService)) {
                    throw new Exception("委托服务类[delegatedClass]需要实现ExportExecuteService接口");
                }
                service = (ExportExecuteService) classObject;
            } else {
                Object classObject = SpringContextHolder.getBean(delegatedClass);
                if (!(classObject instanceof ExportExecuteService)) {
                    throw new Exception("委托服务类[delegatedClass]需要实现ExportExecuteService接口");
                }
                service = (ExportExecuteService) classObject;
            }
            //前置处理
            service.preProcess(task);
            IexpTaskConfig config = service.taskConfig(task);

            // 判断是否为自定义EXCEL下载
            // 如果为自定义EXCEL下载，不进行后续操作
            String jsonParam = task.getJsonParam();
            if (!(StringUtil.isEmpty(jsonParam))) {
                Map map = JsonMapper.fromJsonString(jsonParam, Map.class);
                String extraFlag = map.get("extraFlag") == null ? "N" : (String) map.get("extraFlag");
                if ("Y".equals(extraFlag)) {
                    // 任务完成后置处理
                    service.postProcess(task);
                    return;
                }
            }
            ByteArrayOutputStream os = executeTask(task, config, service);
            String fileName = config.getExportFileName();
            if (config.isUseTimeStamp()) {
                fileName += DateUtils.getDate(config.getTimeStampFormat());
            }
            fileName += "." + config.getExportFileExtName();
            Attachment attachment = fileService.uploadFromInputStream(new ByteArrayInputStream(os.toByteArray()), fileName);
            task.setIexpStatus("iexp_status_success");
            task.setMessage("success");
            task.setFileName(attachment.getFileName());
            task.setAttachment(attachment);
            task.setFileExt(config.getExportFileExtName());
        } catch (ServiceException e) {
            logger.error(e.getMessage(), e);
            task.setMessage(getMessageCodeHelper().getMessage(e.getCode(),e.getData(),""));
            task.setIexpStatus("iexp_status_error");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            task.setMessage(e.getMessage());
            task.setIexpStatus("iexp_status_error");
        }
        // 更新任务状态以及任务日志
        task.setEndDate(new Date());
        // 任务完成后置处理
        service.postProcess(task);
        iexpTaskService.update(task);
    }

    /**
     * 按实体类型直接导出,不需要模板
     *
     * @param task    当前任务对象
     * @param config  当前任务使用的配置类
     * @param service 当前任务的服务类,即执行类
     * @return
     */
    private ByteArrayOutputStream executeTask(IexpTask task, IexpTaskConfig config, ExportExecuteService service) throws Exception {
        // 按实体类型直接导出,不需要模板
        if (service instanceof AbstractEntityExportService) {
            return entityExport(task, config, (AbstractEntityExportService) service);
        }
        // 标准模板导出,需要提供导出模板
        if (service instanceof AbstractTemplateExportService) {
            return templateExport(task, config, (AbstractTemplateExportService) service);
        }
        // 动态模板导出,不需要提供导出模板,但需要提供导出模型
        if (service instanceof AbstractGeneralTemplateExportService) {
            return generalTemplateExport(task, config, (AbstractGeneralTemplateExportService) service);
        }
        // 动态模板导出,不需要提供导出模板,但需要提供导出模型
        if (service instanceof BatchExportService) {
            return batchExport(task, config, (BatchExportService) service);
        }
        return new ByteArrayOutputStream();
    }

    /**
     * 按实体类型直接导出,不需要模板
     *
     * @param task    当前任务对象
     * @param config  当前任务使用的配置类
     * @param service 当前任务的服务类,即执行类
     */
    private ByteArrayOutputStream batchExport(IexpTask task, IexpTaskConfig config, BatchExportService service) throws Exception {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ZipOutputStream zip = null;
        try {
            zip = new ZipOutputStream(os);
            // 裂变任务
            List<IexpTask> iexpTasks = service.loopTask(task);
            ExportExecuteService executeService = service.getExecuteService(task);
            if (iexpTasks != null && !iexpTasks.isEmpty()) {
                for (IexpTask executeTask : iexpTasks) {
                    IexpTaskConfig executeConfig = executeService.taskConfig(executeTask);
                    ByteArrayOutputStream byteArrayOutputStream = executeTask(executeTask, executeService.taskConfig(executeTask), executeService);
                    if (executeTask.getItemList() != null && !executeTask.getItemList().isEmpty()) {
                        if (task.getItemList() == null) {
                            task.setItemList(new ArrayList<>());
                        }
                        task.getItemList().addAll(executeTask.getItemList());
                    }
                    // 添加到zip
                    String fileName = executeConfig.getExportFileName() + DateUtils.getDate("yyyyMMddHHmmss") + "." + executeConfig.getExportFileExtName();
                    zip.putNextEntry(new ZipEntry(fileName));
                    IOUtils.write(byteArrayOutputStream.toByteArray(), zip);
                    zip.closeEntry();
                }
                zip.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                zip.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return os;
    }

    /**
     * 按实体类型直接导出,不需要模板
     *
     * @param task    当前任务对象
     * @param config  当前任务使用的配置类
     * @param service 当前任务的服务类,即执行类
     */
    private ByteArrayOutputStream generalTemplateExport(IexpTask task, IexpTaskConfig config, AbstractGeneralTemplateExportService service) throws Exception {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Map<String, Object> queryData = service.queryData(task);
        new ExportTemplateExcelExecutor()
                .initByModelAndData(config.getExportModel(), queryData)
                .buildExcel()
                .write(os).dispose();
        return os;
    }

    /**
     * 按实体类型直接导出,不需要模板
     *
     * @param task    当前任务对象
     * @param config  当前任务使用的配置类
     * @param service 当前任务的服务类,即执行类
     */
    private ByteArrayOutputStream templateExport(IexpTask task, IexpTaskConfig config, AbstractTemplateExportService service) throws Exception {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Map<String, Object> queryData = service.queryData(task);
        if (StringUtils.isNotBlank(config.getTemplatePath())) {
            new ExportTemplateExcelExecutor()
                    .initByTemplatePathAndData(config.getTemplatePath(), queryData)
                    .configDynamicSheet(config.getDynamicSheets())
                    .configResetCellFormula(config.isResetCellFormula())
                    .buildExcel()
                    .write(os).dispose();
        } else {
            new ExportTemplateExcelExecutor()
                    .initByTemplateNameAndData(config.getTemplateName(), queryData)
                    .configDynamicSheet(config.getDynamicSheets())
                    .configResetCellFormula(config.isResetCellFormula())
                    .buildExcel()
                    .write(os).dispose();
        }
        return os;
    }

    /**
     * 按实体类型直接导出,不需要模板
     *
     * @param task    当前任务对象
     * @param config  当前任务使用的配置类
     * @param service 当前任务的服务类,即执行类
     */
    private ByteArrayOutputStream entityExport(IexpTask task, IexpTaskConfig config, AbstractEntityExportService service) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Class<? extends DataEntity> entityClass = config.getExportEntityClass();
        ExcelExport ee = new ExcelExport(config.getExportFileName() + "数据", entityClass);
        ee.setDataList(service.queryData(task)).write(os).dispose();
        return os;
    }
}
